package com.google.gwt.sample.dynatablerf.server.dao.impl;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Transactional;

import com.google.gwt.sample.dynatablerf.server.dao.IDAOManager;
import com.google.gwt.sample.dynatablerf.server.domain.AbstractEntity;
import com.google.gwt.sample.dynatablerf.server.domain.EntityQueryParams;

public abstract class DAOManager<T extends AbstractEntity, G extends EntityQueryParams>
		implements IDAOManager<T, G> {

	/** JPA Persistence Unit. */
	@PersistenceContext(type = PersistenceContextType.EXTENDED, name = "persistenceUnit")
	private EntityManager em;

	protected abstract String getFindQueryName();

	protected abstract String getCountQueryName();

	public abstract G getEmptyParams(); // Consulta sem parâmetros

	protected abstract Class<T> getEntityClass();

	@Transactional
	public T persist(T entity) {
		em.persist(entity);
		return entity;
	}

	private Query createQuery(String queryName, Map<String, Object> map,
			int startIndex, int maxResult) {
		Query query = em.createNamedQuery(queryName);
		for (String key : map.keySet()) {
			try {
				query.setParameter(key, map.get(key));
			} catch (IllegalArgumentException q) {
				// Se não localizou o parâmetro não levanta a exceção. Lógica
				// feita para o SuggestQuery ( não utiliza todos os parâmetros)
			}

		}
		query.setFirstResult(startIndex);
		query.setMaxResults(maxResult);
		return query;
	}

	@SuppressWarnings("unchecked")
	private List<T> find(String queryName, Map<String, Object> map,
			int startIndex, int maxResult) {
		Query query = createQuery(queryName, map, startIndex, maxResult);
		return query.getResultList();
	}

	private List<T> find(Map<String, Object> map, int startIndex, int maxResult) {
		return find(getFindQueryName(), map, startIndex, maxResult);
	}

	@Override
	public T find(G params) {
		params = getNotNullQuery(params);
		List<T> list = find(params.toParamMap(), 0, 0);
		if (list != null && !list.isEmpty())
			return list.get(0);
		else
			return null;
	}

	private G getNotNullQuery(G params) {
		if (params == null)
			params = getEmptyParams();
		return params;
	}

	@Override
	public List<T> findAll(G params, int startIndex, int maxResult) {
		params = getNotNullQuery(params);
		return find(params.toParamMap(), startIndex, maxResult);
	}

	@Override
	public List<T> findAll(int startIndex, int maxResult) {
		return findAll(null, startIndex, maxResult);
	}

	@SuppressWarnings("rawtypes")
	@Override
	public Long count(G params) {
		params = getNotNullQuery(params);
		List list = find(getCountQueryName(), params.toParamMap(), 0, 0);
		if (list != null && !list.isEmpty())
			return (Long) list.get(0);
		else
			return 0L;
	}

	@Override
	public Long countAll() {
		return count(null);
	}

	@Override
	public T findById(Long id) {
		return em.find(getEntityClass(), id);
	}
}
